package org.finesys.common.security.core.util;

import java.time.temporal.ChronoUnit;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import cn.hutool.core.map.MapUtil;
import lombok.experimental.UtilityClass;

@UtilityClass
public class OAuth2EndpointUtils {

    public final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";


    //获取客户端用户信息
    public OAuth2ClientAuthenticationToken getAuthenticatedClient(Authentication authentication) {
        OAuth2ClientAuthenticationToken clientAuthenticationToken = null;
        if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
            clientAuthenticationToken = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
        }
        if (clientAuthenticationToken != null && clientAuthenticationToken.isAuthenticated()) {
            return clientAuthenticationToken;
        }
        throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
    }


    /**
     * 获取参数信息
     */
    public MultiValueMap<String, String> getParameters(HttpServletRequest request) {
        Map<String, String[]> map = request.getParameterMap();
        MultiValueMap<String, String> paramters = new LinkedMultiValueMap<>(map.size());
        map.forEach((key, values) -> {
            for (String val : values) {
                paramters.add(key, val);
            }
        });
        return paramters;
    }

    /**
     * 抛出异常信息
     */
    public void throwError(String errorCode, String parameterName, String errorUri) {
        OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter:" + parameterName, errorUri);
        throw new OAuth2AuthenticationException(error);
    }

    /**
     * 校验参数是否合法
     */
    public void checkMustParameters(MultiValueMap<String, String> parameters, String... parameter) {
        for (String p : parameter) {
            String cap = parameters.getFirst(p);
            if (!StringUtils.hasText(cap) || parameters.get(p).size() != 1) {
                throwError(OAuth2ErrorCodes.INVALID_REQUEST, p, ACCESS_TOKEN_REQUEST_ERROR_URI);
            }
        }
    }

    public void checkOptionalParameters(MultiValueMap<String, String> parameters, String... parameter) {
        for (String p : parameter) {
            String cap = parameters.getFirst(p);
            if (StringUtils.hasText(cap) && parameters.get(p).size() != 1) {
                throwError(OAuth2ErrorCodes.INVALID_REQUEST, p, ACCESS_TOKEN_REQUEST_ERROR_URI);
            }
        }
    }

    /**
     * 格式化输出token 信息
     *
     * @param oAuth2Authorization 用户认证信息
     * @param claims              扩展信息
     */

    public OAuth2AccessTokenResponse sendAccessTokenResponse(OAuth2Authorization oAuth2Authorization, Map<String, Object> claims) {
        OAuth2AccessToken auth2AccessToken = oAuth2Authorization.getAccessToken().getToken();
        OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(auth2AccessToken.getTokenValue())
        .tokenType(auth2AccessToken.getTokenType())
        .scopes(auth2AccessToken.getScopes());
        if (auth2AccessToken.getExpiresAt() != null && auth2AccessToken.getIssuedAt() != null) {
            builder.expiresIn(ChronoUnit.SECONDS.between(auth2AccessToken.getIssuedAt(), auth2AccessToken.getExpiresAt()));
        }
        if(oAuth2Authorization.getRefreshToken()!=null){
            OAuth2RefreshToken refreshToken = oAuth2Authorization.getRefreshToken().getToken();
            if (refreshToken != null) {
                builder.refreshToken(refreshToken.getTokenValue());
            }
        }

        if (MapUtil.isNotEmpty(claims)) {
            builder.additionalParameters(claims);
        }
        return builder.build();
    }
}
